package com.thientan.export;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;


/**
 * Base implementation of a {@link Request}.
 * 
 * @author lnt
 * @param <REQ>
 *            Actual request type.
 */
public abstract class AbstractRequest<REQ extends AbstractRequest<?>> implements Request, Serializable {
    private static final long serialVersionUID = 4863922712063647960L;
    protected String id;
    protected Map<String, Object> additionalProperties = new HashMap<String, Object>();
    protected String eventName;

    /**
     * {@inheritDoc}
     */
    @Override
    public final String getId() {
        if (StringUtils.isEmpty(id)) {
            id = createId();
        }
        return id;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final String getOwnerEvent() {
        return eventName;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final Map<String, Object> getAdditionalProperties() {
        return additionalProperties;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final <T> T getAdditionalProperty(String name, Class<T> resultType) throws ClassCastException {
        Object val = getAdditionalProperties().get(name);
        if (val != null) {
            return resultType.cast(val);
        } else {
            return null;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final <T> T getAdditionalPropertyStrict(String name, Class<T> resultType) {
        Object val = getAdditionalProperties().get(name);

        return resultType.cast(val);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public REQ withAdditionalProperties(Map<String, Object> propertyMap) {
        this.additionalProperties.clear();
        if (propertyMap != null) {
            this.additionalProperties.putAll(propertyMap);
        }
        return getMe();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public REQ withAdditionalProperty(String property, Object value) {
        this.additionalProperties.put(property, value);
        return getMe();
    }

    /**
     * Sets owner event of this request.
     * 
     * @param event
     * @return
     */
    public REQ ownerEvent(String event) {
        this.eventName = event;
        return getMe();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return createId();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final boolean equals(Object obj) {
        if (this == obj) {
            return true;
        } else if (obj == null) {
            return false;
        } else if (obj instanceof Request) {
            return ObjectUtils.equals(getId(), ((Request) obj).getId());
        } else {
            return super.equals(obj);
        }
    }

    /**
     * Creates a unique ID value of this request. By default, this method returns empty string which means subclasses
     * are free to override and return a better ID.
     * 
     * @return Created ID or empty string (allow ID to be set manually by subclasses).
     */
    protected String createId() {
        return "";
    }

    @SuppressWarnings("unchecked")
    protected final REQ getMe() {
        return (REQ) this;
    }
}
